#include "StdAfx.h"
#include "LitEditorRenderWidget.h"
#include "LitRoot.h"
#include <qmessagebox.h>

namespace Lit
{
	namespace Editor
	{
		_OgreWidget::_OgreWidget(QWidget *parent)
			: QWidget(parent)
			, mogreRoot(0)
			, mogreSceneManager(0)
			, mogreRenderWindow(0)
			, mogreViewport(0)
			, mogreCamera(0)
		{
			//setAttribute(Qt::WA_OpaquePaintEvent);
			setAttribute(Qt::WA_NoSystemBackground);
			setAttribute(Qt::WA_PaintOnScreen);
			setMinimumSize(240,240);
			setMouseTracking( true );
		}
 
		_OgreWidget::~_OgreWidget()
		{
	
			if(mogreRoot)
			{
				for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
					if( (*i) )
					{
						mogreRoot->removeFrameListener( (*i) );
						(*i)->destroyScene();
					}
				mListeners.clear();
				emit ogreShutdown();
				if(mogreRenderWindow)
				{
					mogreRenderWindow->removeAllViewports();
				}
				mogreRoot->detachRenderTarget(mogreRenderWindow);
 
				if(mogreSceneManager)
				{
					mogreRoot->destroySceneManager(mogreSceneManager);
				}
 
				delete mogreRoot, mogreRoot = NULL;
			}
		}
		void _OgreWidget::addListener( RenderWidgetListener * listener ){ mListeners.push_back( listener ); }
		void _OgreWidget::removeListener( RenderWidgetListener * listener ){ for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) if( *i == listener ) { mListeners.erase( i ); return; } }
		bool _OgreWidget::hasListener( RenderWidgetListener * lstnr ) const{ for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) if( *i == lstnr ) return true; return false; }

		void _OgreWidget::mousePressEvent( QMouseEvent*evt )
		{
			QPoint p(evt->pos());
			Qt::MouseButtons mb(evt->buttons());
			for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
				if( (*i) )
				{
					(*i)->mousePressed(p,mb);
				}
		}
		void _OgreWidget::wheelEvent(QWheelEvent* evt){
			int d = evt->delta();
			for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
				if( (*i) )
				{
					(*i)->mouseScrolled( d );
				}
		}
		void _OgreWidget::mouseMoveEvent( QMouseEvent*evt )
		{
			QPoint p(evt->pos());
			Qt::MouseButtons mb(evt->buttons());
			for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
				if( (*i) )
				{
					(*i)->mouseMoved(p,mb);
				}
		}
		void _OgreWidget::mouseReleaseEvent( QMouseEvent*evt )
		{
			QPoint p(evt->pos());
			Qt::MouseButtons mb(evt->buttons());
			for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
				if( (*i) )
				{
					(*i)->mouseReleased(p,mb);
				}
		}
 
		void _OgreWidget::setBackgroundColor(QColor c)
		{
			if(mogreViewport)
			{
				Ogre::ColourValue mogreColour;
				mogreColour.setAsARGB(c.rgba());
				mogreViewport->setBackgroundColour(mogreColour);
			}
		}

		void _OgreWidget::moveEvent(QMoveEvent *e)
		{
			QWidget::moveEvent(e);
 
			if(e->isAccepted() && mogreRenderWindow)
			{
				mogreRenderWindow->windowMovedOrResized();
				update();
			}
		}
 
		QPaintEngine *_OgreWidget:: paintEngine() const
		{
			return 0;
		}
 
		void _OgreWidget::paintEvent(QPaintEvent *e)
		{
		//    mogreRoot->_fireFrameStarted();
		//    mogreRenderWindow->update();
		//    mogreRoot->_fireFrameEnded();
 
		//	mogreRoot->renderOneFrame();
		//	mogreRenderWindow->update();
 
		//    e->accept();
			if( !mogreRoot ) return;
			if( !mogreRenderWindow ) return;
			try
			{
				mogreRoot->_fireFrameStarted();
				mogreRenderWindow->update();
				mogreRoot->_fireFrameRenderingQueued();
				mogreRoot->_fireFrameEnded();
		
				update();
			}
			catch( std::exception& e )
			{
				if( QMessageBox::question(this, "Rendering Error", (std::string("cannot render frame because: ")+e.what()).c_str(), QMessageBox::Retry|QMessageBox::Abort) == QMessageBox::Retry )
				{
				}
				else
				{
					throw;
				}
			}
			catch( ... )
			{
				if( QMessageBox::question(this, "Rendering Error", "Unknown error while rendering frame", QMessageBox::Retry|QMessageBox::Abort) == QMessageBox::Retry )
				{
				}
				else
				{
					throw;
				}
			}
		}
 
		void _OgreWidget::resizeEvent(QResizeEvent *e)
		{
			QWidget::resizeEvent(e);
 
			if(e->isAccepted()&&e)
			{
				const QSize &newSize = e->size();
				if(mogreRenderWindow)
				{
					mogreRenderWindow->resize(newSize.width(), newSize.height());
					mogreRenderWindow->windowMovedOrResized();
				}
				if(mogreCamera)
				{
					Ogre::Real aspectRatio = Ogre::Real(newSize.width()) / Ogre::Real(newSize.height());
					mogreCamera->setAspectRatio(aspectRatio);
				}
			}
		}
 
		void _OgreWidget::showEvent(QShowEvent *e)
		{
					QWidget::showEvent(e);
			if(!mogreRoot)
			{
				try
				{
					initOgreSystem();
					for( auto i=mListeners.begin(); i!=mListeners.end(); i++ ) 
						if( (*i) ){
							(*i)->createScene();
							mogreRoot->addFrameListener( (*i) );
						}
				}
				catch( std::exception &e )
				{
					QMessageBox::warning( parentWidget(), "Initialisation Error", e.what() );
				}
			} 
		}
 
		void _OgreWidget::initOgreSystem()
		{
			try
			{
		#ifndef NDEBUG
				mogreRoot = new Ogre::Root( "plugins_d.cfg", "ogre_d.cfg", "ogre_d.log" );
		#else
				mogreRoot = new Ogre::Root( "plugins.cfg", "ogre.cfg", "ogre.log" );
		#endif

				//-------------------------------------------------------------------------------------
				// setup resources
				// Load resource paths from config file
				Ogre::ConfigFile cf;
		#ifndef NDEBUG
				cf.load("resources_d.cfg");
		#else
				cf.load("resources.cfg");
		#endif

				// Go through all sections & settings in the file
				Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

				Ogre::String secName, typeName, archName;
				while (seci.hasMoreElements())
				{
					secName = seci.peekNextKey();
					Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
					Ogre::ConfigFile::SettingsMultiMap::iterator i;
					for (i = settings->begin(); i != settings->end(); ++i)
					{
						typeName = i->first;
						archName = i->second;
						Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
							archName, typeName, secName);
					}
				}
			}
			catch( std::exception & e )
			{
				throw std::runtime_error(std::string("Cannot initialise Ogre::Root because: ")+e.what());
			}
			try
			{
				Ogre::RenderSystem *renderSystem = mogreRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
				mogreRoot->setRenderSystem(renderSystem);
				mogreRoot->initialise(false);
			}
			catch( std::exception & e )
			{
				throw std::runtime_error(std::string("Cannot initialise Ogre::RenderSystem because: ")+e.what());
			}
	
			try
			{
				mogreSceneManager = mogreRoot->createSceneManager(Ogre::ST_GENERIC);
 
				Ogre::NameValuePairList viewConfig;
				Ogre::String widgetHandle;
 
			#ifdef Q_OS_WIN
				widgetHandle = Ogre::StringConverter::toString((size_t)((HWND)winId()));
			#else
				QWidget *q_parent = dynamic_cast <QWidget *> (parent());
				QX11Info xInfo = x11Info();

				widgetHandle = Ogre::StringConverter::toString ((unsigned long)xInfo.display()) +
					":" + Ogre::StringConverter::toString ((unsigned int)xInfo.screen()) +
					":" + Ogre::StringConverter::toString ((unsigned long)q_parent->winId());
			#endif
				viewConfig["externalWindowHandle"] = widgetHandle;
 
			#ifdef Q_OS_WIN
				//positive integer for W32 (HWND handle) - According to Ogre Docs
				widgetHandle = Ogre::StringConverter::toString((unsigned int)(winId()));
				viewConfig["externalWindowHandle"] = widgetHandle;

			#endif
 
			#if defined(Q_OS_UNIX)
					//poslong:posint:poslong:poslong (display*:screen:windowHandle:XVisualInfo*) for GLX - According to Ogre Docs
 
				QWidget *q_parent = dynamic_cast <QWidget *> (parent());
				QX11Info info = x11Info();
					widgetHandle  = Ogre::StringConverter::toString((unsigned long)info.display());
					widgetHandle += ":";
					widgetHandle += Ogre::StringConverter::toString((unsigned int)info.screen());
					widgetHandle += ":";
					widgetHandle += Ogre::StringConverter::toString((unsigned long)q_parent->winId());
					//widgethandle += ":";
					//widgethandle += Ogre::StringConverter::toString((unsigned long)(info.visual()));
 
					viewConfig["parentWindowHandle"] = widgetHandle;
			#endif
 
 
			#if defined(Q_WS_MAC)

				widgetHandle = Ogre::StringConverter::toString((unsigned int)(winId()));
				viewConfig["macAPI"] = "cocoa";
				viewConfig["macAPICocoaUseNSView"] = "true";

			#endif
				mogreRenderWindow = mogreRoot->createRenderWindow("Ogre rendering window",
					width(), height(), false, &viewConfig);
			}
			catch( std::exception & e )
			{
				throw std::runtime_error(std::string("Cannot initialise Ogre::RenderWindow because: ")+e.what());
			}
			try
			{
				Root::registerResourceManagers( );
				Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
				mogreCamera = mogreSceneManager->createCamera("myCamera");
 
				mogreViewport = mogreRenderWindow->addViewport(mogreCamera);
				mogreViewport->setBackgroundColour(Ogre::ColourValue(0.0f,0.0f,0.0f));
				mogreCamera->setAspectRatio(Ogre::Real(width()) / Ogre::Real(height()));
	
				mogreCamera->setPosition( 100.0f, 5.0f, 100.0f );
				mogreCamera->lookAt( 0.0f,0.0f,0.0f );
				mogreCamera->setNearClipDistance( 0.01f );
			}
			catch( std::exception & e )
			{
				throw std::runtime_error(std::string("Cannot create scene because: ")+e.what());
			}

			emit ogreInitialise();
		}

		RenderWidget::RenderWidget(QWidget*parent) : _OgreWidget(parent)
		{
		}


		RenderWidget::~RenderWidget(void)
		{

		}
	};
};
